package lib;

import java.lang.Thread;
import java.net.Socket;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

public abstract class Actor implements Runnable{
	private Thread behaviourThread;
	private boolean threadIsStarted;
	protected ListenSocket listenSocket;
	protected ActorAddress superActor;
	protected ActorAddress myAddress;
	protected String actorType;
	protected boolean standardRoutineIsUsed;

	public Actor(ActorAddress superActor,ActorAddress myAddress,String actorType) throws Exception{
		this.superActor=superActor;
		this.myAddress=myAddress;
		this.actorType=actorType;
		this.listenSocket=this.myAddress.getListenSocket(this.actorType, superActor);
		this.behaviourThread=new Thread(this);
		this.threadIsStarted=false;
		this.standardRoutineIsUsed=true;
	}
	public void run(){
		try{
			boolean continues=true,contHM=true,contSR=true;
			Message msg;
			do{
				if(standardRoutineIsUsed){
					contSR=standardRoutine();
					msg=listenSocket.getMessage();
					
					if (msg!=null){
						if (!(msg.getOperation().equals("Terminate")&&(!msg.getSrcActorType().equals("SuperActor")))){
							//Don't transmit a terminate message if it isn't from the superactor
							contHM=handleMessage(msg);
						}
					}
					continues=contSR&&contHM;
				}else{
					msg=listenSocket.getMessageBlocking();
					continues=handleMessage(msg);
				}
			}while(continues);
		}catch(Exception e){
			try {
				if (!actorType.equals("SuperActor")){
					System.out.println(actorType+": Exception caught:");
					e.printStackTrace();
					System.out.println("This actor will die, informing the superActor.");
					Message msg = new Message(this.myAddress, this.actorType,"SuperActor", "Is dead", null);
					sendMsg(msg);
				} else {
					e.printStackTrace();
					System.out.println("Exception on the superactor, system panic, the impossible happened!");
				}
			}
			catch(Exception e1){
				e1.printStackTrace();
			}
		}
	}
	protected void sendMsg(Message msg) throws Exception{
		Socket conn=superActor.getSocket();
		ObjectOutputStream objOut=new ObjectOutputStream(conn.getOutputStream());
		objOut.writeObject(msg);
		conn.close();
	}
	protected void startActor(){
		if (!threadIsStarted) {
			this.behaviourThread.start();
			this.threadIsStarted=true;
		}
	}
	/* Functions defining the actor's Behaviour */
	abstract protected boolean standardRoutine() throws Exception;
	abstract protected boolean handleMessage(Message msg) throws Exception;
}
